package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.ABC;
import com.file.bo.mail.ABCTran;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.BasicExtractionAlgorithm;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ABCPdfParser extends BasePdfParser {


    public ResponseData<String> parseABCPdfToJson(String daId, String filePath) {
        log.info("parseABCPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            ABC abc = parseABCPdf(filePath);
            json = JsonUtils.convertObjectToJson(abc);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseABCPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseABCPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private ABC parseABCPdf(String filePath) {
        String pdfHeaderText = parsePdfHeaderText(filePath);
        ABC abc = parseABCHeader(pdfHeaderText);
        List<List<String>> rowList = parseFileToRowList(filePath);
//        for (List<String> cellList : rowList) {
//            System.out.println(cellList);
//        }
        parseListToBO(rowList, abc);

        return abc;
    }

    private void parseListToBO(List<List<String>> rowList, ABC abc) {
        List<ABCTran> abcTrans = new ArrayList<>();
        abc.setAbcTrans(abcTrans);

        rowList = reSetRowList_SplitTranDateAndTranTime(rowList); //NOSONAR
        rowList = reSetRowList_Split_CounterPartyInfoAndJournalNumber(rowList); //NOSONAR
        rowList = reSetRowList_removeExtraColumnAfterTranChannel(rowList); //NOSONAR
        rowList = reSetRowList_resetValueForCrossRow(rowList); //NOSONAR

        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).get(0).contains("该交易明细因不可预测")) { //NOSONAR
                continue;
            } else if (StringUtils.equals(rowList.get(i).get(0), "交易日期")) {  //NOSONAR
                continue;
            } else if (StringUtils.isEmpty(rowList.get(i).get(0))) {  //NOSONAR
                continue;
            }

            ABCTran abcTran = new ABCTran();
            abcTran.setTranDate(rowList.get(i).get(0));
            abcTran.setTranTime(rowList.get(i).get(1));
            abcTran.setTranSummary(rowList.get(i).get(2));
            abcTran.setTranAmt(rowList.get(i).get(3));
            abcTran.setBalance(rowList.get(i).get(4));
            abcTran.setCounterPartyInfo(rowList.get(i).get(5));
            abcTran.setJournalNumber(rowList.get(i).get(6));
            abcTran.setTranChannel(rowList.get(i).get(7));
            if (rowList.get(i).size() > 8) {
                abcTran.setTranComment(rowList.get(i).get(8));
            } else {
                abcTran.setTranComment("");
            }
            abcTrans.add(abcTran);
        }
//        parseCounterPartyInfoAndTranComment(filePath, abcTrans);
    }


    private List<List<String>> reSetRowList_Split_CounterPartyInfoAndJournalNumber(List<List<String>> rowList) {
        // ["交易日期","交易时间","交易摘要","交易金额","本次余额","对手信息 日 志 号","","交易渠道","交易附言"]
        // [交易日期, 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息 日 志 号, 交易渠道, 交易附言]
        boolean needSplit = false;
        boolean addColumn = false;
        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).size() < 7) {
                continue;
            }
            if (rowList.get(i).get(5).contains("对手信息 日 志 号")) {
                needSplit = true;
                if (rowList.get(i).get(6).contains("交易渠道")) {
                    addColumn = true;
                } else {
                    addColumn = false;
                }
                continue;
            } else if (rowList.get(i).get(5).contains("对手信息") && rowList.get(i).get(6).contains("日 志 号")) {
                needSplit = false;
            }

            if (needSplit) {
                String journalNumber = "";
                String counterPartyInfo = "";
                List<String> row = rowList.get(i);
                if (row.get(5).matches(".*[A-Z]*\\d{1,20}")) {
                    Matcher matcher2 = Pattern.compile("[A-Z]*\\d{1,20}").matcher(row.get(5));

                    while (matcher2.find()) {
                        journalNumber = matcher2.group();
                    }
                    counterPartyInfo = row.get(5).replace(journalNumber, "").trim();

                    //  日志号出现中文:"0847尾号商户转入"
                } else {
                    if (StringUtils.isBlank(row.get(0))) {
                        continue;
                    }
                    String[] array = row.get(5).split(" ");
                    if (array.length >= 2) {
                        journalNumber = array[array.length - 1];
                        counterPartyInfo = row.get(5).replace(journalNumber, "").trim();
                    }
                }
                //[交易日期, 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息 日 志 号, 交易渠道, 交易附言]
                if (addColumn) {
                    rowList.get(i).set(5, counterPartyInfo);
                    rowList.get(i).add(6, journalNumber);
                    // ["交易日期","交易时间","交易摘要","交易金额","本次余额","对手信息 日 志 号","","交易渠道","交易附言"]
                } else {
                    rowList.get(i).set(5, counterPartyInfo);
                    rowList.get(i).set(6, journalNumber);
                }
            } else {
                // 对手信息和日志号有可能合并到一起
                if (StringUtils.isBlank(rowList.get(i).get(0))) {
                    continue;
                }
                String[] array = rowList.get(i).get(5).split(" ");
                if (array.length >= 2 && StringUtils.isEmpty(rowList.get(i).get(6))) {
                    String journalNumber = array[array.length - 1];
                    String counterPartyInfo = rowList.get(i).get(5).replace(journalNumber, "").trim();
                    rowList.get(i).set(5, counterPartyInfo);
                    rowList.get(i).set(6, journalNumber);
                }
            }
        }
        return rowList;
    }

    private List<List<String>> reSetRowList_SplitTranDateAndTranTime(List<List<String>> rowList) {
        //[交易日期 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息, 日 志 号, 交易渠道, , 交易附言]
        boolean needSplitTranDateAndTranTime = false;

        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).size() < 2) {
                continue;
            }
            if (rowList.get(i).get(0).contains("交易日期 交易时间")) {
                needSplitTranDateAndTranTime = true;
            } else if (rowList.get(i).get(0).contains("交易日期") && rowList.get(i).get(1).contains("交易时间")) {
                needSplitTranDateAndTranTime = false;
            }

            if (needSplitTranDateAndTranTime) {
                String tranDateAndTranTime = rowList.get(i).get(0);
                if (StringUtils.isEmpty(tranDateAndTranTime)) {
                    rowList.get(i).add(1, "");
                } else {
                    String[] array = tranDateAndTranTime.split(" ");
                    if (array.length == 2) {
                        rowList.get(i).set(0, array[0]);
                        rowList.get(i).add(1, array[1]);
                    } else if (array.length == 1) {
                        rowList.get(i).add(1, "");
                    }
                }
            }

            // 兼容交易日期串行问题
            // 使用正则表达式匹配末尾的8位数字，并获取前面的文本
            String dateCell = rowList.get(i).get(0);
            Pattern pattern = Pattern.compile("(.+)(\\d{8})$");
            Matcher matcher = pattern.matcher(dateCell);
            if (matcher.find()) {

                // 获取8位数字前面的文本
                String prefixText = matcher.group(1).trim();
                String tranDate = matcher.group(2);
                // 将前面的文本和日期分别设置到不同的列中
                rowList.get(i).set(0, tranDate);
                // 无法得知是哪列信息串行，暂时忽略
//                if (rowList.get(i).size() > 6) {
//                    rowList.get(i).set(6, prefixText);
//                }
            }
        }

//        if (needSplitTranDateAndTranTime) {
//            for (int i = 0; i < rowList.size(); i++) {
//                String tranDateAndTranTime = rowList.get(i).get(0);
//                if (StringUtils.isEmpty(tranDateAndTranTime)) {
//                    rowList.get(i).add(1, "");
//                } else {
//                    String[] array = tranDateAndTranTime.split(" ");
//                    if (array.length == 2) {
//                        rowList.get(i).set(0, array[0]);
//                        rowList.get(i).add(1, array[1]);
//                    } else if (array.length == 1) {
//                        rowList.get(i).add(1, "");
//                    }
//                }
//            }
//        }

        return rowList;
    }


    private List<List<String>> reSetRowList_resetValueForCrossRow(List<List<String>> rowList) {
//        [, , , , , 武汉合众易宝科技有限, , , NG2024010200113923426084530210000抖音生活服务]
//        [20240102, 144713, 代付, +2174.34, 6373.11, , 430829307, 电子商务, ]
//        [, , , , , 公司, , , 商家提现合众易宝]
        String wrapFlag = "wrapFlag";
        for (int i = 0; i < rowList.size(); i++) {
            // 处理对手信息，日志号，交易附言换行问题
            int[] wrapIndexList = {5, 6, 8};
            for (int wrapIndex : wrapIndexList) {
                if (rowList.get(i).size() > wrapIndex
                        && i > 0 && i < rowList.size() - 1   //NOSONAR
                        && StringUtils.isNotBlank(rowList.get(i).get(0))    //NOSONAR
                        && StringUtils.isEmpty(rowList.get(i - 1).get(0))   //NOSONAR
                        && StringUtils.isEmpty(rowList.get(i + 1).get(0))) {   //NOSONAR
                    int index = 1;
                    // 无法确定换了几行，加到新起一行为止
                    while (i > index - 1 &&  i < rowList.size() - index     //NOSONAR
                            && rowList.get(i - index ).size() > wrapIndex   //NOSONAR
                            && rowList.get(i + index ).size() > wrapIndex   //NOSONAR
                            && StringUtils.isEmpty(rowList.get(i + index).get(0))   //NOSONAR
                            && StringUtils.isEmpty(rowList.get(i - index).get(0))) {    //NOSONAR
                        String preValue = rowList.get(i - index).get(wrapIndex);    //NOSONAR
                        String nextValue = rowList.get(i + index).get(wrapIndex);   //NOSONAR
                        if (StringUtils.isNotBlank(preValue) && StringUtils.isNotBlank(nextValue)) {
                            // 特殊串行case处理
                            String currentValue = rowList.get(i).get(wrapIndex);    //NOSONAR
                            if (StringUtils.isNotBlank(currentValue) && currentValue.length() < preValue.length()) {
                                break;
                            }
                            if (!preValue.equals(wrapFlag + wrapIndex)) {
                                rowList.get(i).set(wrapIndex, preValue + currentValue + nextValue);
                                rowList.get(i + index).set(wrapIndex, wrapFlag + wrapIndex);
                            }
                        }
                        index++;
                    }
                }
            }
        }
        return rowList;
    }

    private List<List<String>> reSetRowList_removeExtraColumnAfterTranChannel(List<List<String>> rowList) {

        //[交易日期, 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息, 日 志 号, 交易渠道, , 交易附言]
        boolean needRemoveExtraColumnAfterTranChannel = false;

        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).size() >= 9 && rowList.get(i).get(7).contains("交易渠道") && StringUtils.isBlank(rowList.get(i).get(8))) {
                needRemoveExtraColumnAfterTranChannel = true;

            } else if (rowList.get(i).size() >= 9 && rowList.get(i).get(7).contains("交易渠道") && rowList.get(i).get(8).contains("交易附言")) {
                needRemoveExtraColumnAfterTranChannel = false;
            }
            if (needRemoveExtraColumnAfterTranChannel) {
                rowList.get(i).remove(8); //NOSONAR
            }
        }

        boolean needRemoveExtraColumn = false;
        // [交易日期, 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息 日 志 号, , , 交易渠道, 交易附言]
        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).get(0).contains("交易日期")) {
                if (rowList.get(i).size() >= 9 && rowList.get(i).get(8).contains("交易渠道") && StringUtils.isBlank(rowList.get(i).get(7))) {
                    needRemoveExtraColumn = true;
                } else {
                    needRemoveExtraColumn = false;
                }
            }
            if (needRemoveExtraColumn) {
                rowList.get(i).remove(7); //NOSONAR
            }
        }

//     [交易日期, 交易时间, 交易摘要, 交易金额, 本次余额, 对手信息, , 日 志 号, 交易渠道, 交易附言]
        for (int i = 0; i < rowList.size(); i++) {
            if (rowList.get(i).get(0).contains("交易日期")) {
                if (rowList.get(i).size() >= 9 && rowList.get(i).get(7).contains("日 志 号") && StringUtils.isBlank(rowList.get(i).get(6))) {
                    needRemoveExtraColumn = true;
                } else {
                    needRemoveExtraColumn = false;
                }
            }
            if (needRemoveExtraColumn) {
                rowList.get(i).remove(6); //NOSONAR
            }
        }


        return rowList;
    }

    private List<List<String>> parseFileToRowList(String filePath) {
        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (!tablesOnPage.isEmpty()) {
                    detectedTables.put(page.getPageNumber(), tablesOnPage);
                } else {
                    // 适配部分pdf 表格位置识别失败设置默认值
                    float top = findTextBottom(pdfDocument, page.getPageNumber(), "交易时间");
                    Rectangle rectangle = new Rectangle(top-10, 0, 600, 800);
                    detectedTables.put(page.getPageNumber(), Collections.singletonList(rectangle));
                }
            }

            // 6.通过table位置获取表格具体内容
            BasicExtractionAlgorithm bea = new BasicExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());
                // 默认每页只有一个表格，因此获取第0个rectangle/
                Rectangle rectangle = null; //NOSONAR
                List<Rectangle> rectangleList = entry.getValue();
                for (Rectangle r : rectangleList) {
                    double rectangleHeight = r.getHeight();
                    if (Double.compare(rectangleHeight, 0) > 0) {
                        rectangle = r;
                        break;
                    }
                }
                if (rectangle == null) {
                    throw new RuntimeException("rectangle is null.");
                }

                float top = findTextBottom(pdfDocument, page.getPageNumber(), "交易时间");
                if (page.getPageNumber()!=1){
                    rectangle.setTop(top-10);
                }
                rectangle.setBottom((float) page.getHeight());
                rectangle.setRight((float) page.getWidth());

                Page area = page.getArea(rectangle);
                List<Table> tableList = null;
                try {
                    tableList = bea.extract(area);
                } catch (Exception e) {
                    log.info("rectangle extract table fail.");
                    continue;
                }
                // 如果每页有多个表格，解析每一个table
                for (Table table : tableList) {
                    for (int i = 0; i < table.getRowCount(); i++) {
                        List<String> cellList = new ArrayList<>();//一条记录，每个单元格占一个元素
                        for (int j = 0; j < table.getColCount(); j++) {
                            cellList.add(table.getCell(i, j).getText(false));
                        }
                        rowList.add(cellList);
                    }
                }

            }
            return rowList;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static float findTextBottom(PDDocument document, int pageIndex, String searchText) throws IOException {
        float[] y = new float[1];
        PDFTextStripper stripper = new PDFTextStripper() {
            @Override
            protected void writeString(String text, List<TextPosition> textPositions) {
                StringBuilder sb = new StringBuilder();
                for (TextPosition tp : textPositions) {
                    sb.append(tp.getUnicode());
                }
                if (sb.toString().contains(searchText)) {
                    TextPosition tp = textPositions.get(0);
                    y[0] = tp.getY();
                }
            }
        };
        stripper.setSortByPosition(true);
        stripper.setStartPage(pageIndex);
        stripper.setEndPage(pageIndex);
        stripper.getText(document);
        return y[0];
    }

    private ABC parseABCHeader(String pdfHeaderText) {
        ABC abc = new ABC();
        String name = "";
        if (pdfHeaderText.contains("⼾名：") && pdfHeaderText.contains("账⼾：")) {
            name = pdfHeaderText.substring(pdfHeaderText.indexOf("⼾名：") + 3, pdfHeaderText.indexOf("账⼾：")).trim();
        } else if (pdfHeaderText.contains("户名：") && pdfHeaderText.contains("账户：")) {
            name = pdfHeaderText.substring(pdfHeaderText.indexOf("户名：") + 3, pdfHeaderText.indexOf("账户：")).trim();
        }
        String account = "";
        if (pdfHeaderText.contains("账⼾：")) {
            account = pdfHeaderText.substring(pdfHeaderText.indexOf("账⼾：") + 3, pdfHeaderText.indexOf("币种：")).trim();
        } else if (pdfHeaderText.contains("账户：")) {
            account = pdfHeaderText.substring(pdfHeaderText.indexOf("账户：") + 3, pdfHeaderText.indexOf("币种：")).trim();
        }
        String currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币种：") + 3, pdfHeaderText.indexOf("钞汇标识："))
                .trim();

        String cashExchangeIndicator = "";
        String transDetailPeriod = "";
        if (pdfHeaderText.contains("起⽌⽇期：")) {
            cashExchangeIndicator = pdfHeaderText.substring(pdfHeaderText.indexOf("钞汇标识：") + 5, pdfHeaderText.indexOf("起⽌⽇期：")).trim();
            if (pdfHeaderText.contains("电⼦流⽔号：")) {
                transDetailPeriod = pdfHeaderText.substring(pdfHeaderText.indexOf("起⽌⽇期：") + 5, pdfHeaderText.indexOf("电⼦流⽔号：")).trim();
            }
        } else if (pdfHeaderText.contains("起止日期：")) {
            cashExchangeIndicator = pdfHeaderText.substring(pdfHeaderText.indexOf("钞汇标识：") + 5, pdfHeaderText.indexOf("起止日期：")).trim();
            if (pdfHeaderText.contains("电子流水号：")) {
                transDetailPeriod = pdfHeaderText.substring(pdfHeaderText.indexOf("起止日期：") + 5, pdfHeaderText.indexOf("电子流水号：")).trim();
            }
        }
        String serialNo = "";
        if (pdfHeaderText.contains("电⼦流⽔号：") && pdfHeaderText.contains("交易⽇期")) {
            serialNo = pdfHeaderText.substring(pdfHeaderText.indexOf("电⼦流⽔号：") + 6, pdfHeaderText.indexOf("交易⽇期")).trim();
        } else if (pdfHeaderText.contains("电子流水号：") && pdfHeaderText.contains("交易日期")) {
            serialNo = pdfHeaderText.substring(pdfHeaderText.indexOf("电子流水号：") + 6, pdfHeaderText.indexOf("交易日期")).trim();
        }

        abc.setName(name);
        abc.setAccount(account);
        abc.setCurrency(currency);
        abc.setCashExchangeIndicator(cashExchangeIndicator);
        abc.setTransDetailPeriod(transDetailPeriod);
        abc.setSerialNo(serialNo);
        return abc;
    }


    public static void main(String[] args) throws IOException {
        ABCPdfParser abcPdfParser = new ABCPdfParser();
        String json = "";
        json = abcPdfParser.parseABCPdfToJson("", "D:\\data\\file\\beehive-abc\\zd28wckn1872479494371577856_f498c1ef5a64d0ff9b3dc00c33d26321_beehive-abc_jyls-0.pdf").getData();
        System.out.println(json);
//        json = abcPdfParser.parseABCPdfToJson("", "D:\\data\\file\\beehive-abc\\zd4o0dzo1929733585822756864_c7b71089bf7b2ae921fedc27ca3a7921_beehive-abc_jyls-0.pdf").getData();
//        System.out.println(json);





    }
}